#include "stdafx.h"

struct Token;
#pragma once

using namespace std;
using namespace boost::spirit;
using namespace phoenix;

struct tahogen_grammar : public grammar<tahogen_grammar>
{
	tahogen_grammar() : tokens(std::vector<Token>())
	{
	}
	tahogen_grammar(std::vector<Token>& tokens_) : tokens(tokens_)
	{

	}
    template <typename ScannerT>
    struct definition
    {
        
        definition(tahogen_grammar const& self)  
		{ 
			
			top			     = +(markup_top | lexeme_d[literal_sequence]) >> !top;

			/* Literal Grammar */
			literal_char	 = lexeme_d[print_p | space_p | range_p('\253', '\254')] - ("<%" | include_top | open);
			
			literal_chars	 = +literal_char;
			literal_sequence = ((+((literal_char) - ("<%" | include_top) )) )[push_back(var(self.tokens), construct_<Token>(LiteralText, construct_<std::string>(arg1, arg2)))];
			//literal_top      = literal_sequence;

			/* Directive Grammar */
			directive_term  = alnum_p - "%>";
			directive		= ((('@' >> !space_p >> (directive_name) >> +space_p >> *attribute))[push_back(var(self.tokens), construct_<Token>(Directive, construct_<std::string>(arg1, arg2)))] - "%>") || *space_p;
			directive_name  = +(directive_term);
			attribute       = attr_name >> (!space_p >> "=") >> ( (!space_p >> attr_value)) >> *space_p;
			attr_name       = (+directive_term);
			attr_value      = str_p("\"\"") | confix_p('\"', (+print_p), '\"');
			directive_top	= ("<%"  >> (directive) >> "%>") >> *(+space_p >> directive_top);
			
			/* Code Grammar */
			code_sequence = (+(anychar_p - "%>"))[push_back(var(self.tokens), construct_<Token>(LiteralCode, construct_<std::string>(arg1, arg2)))];
			code_top	  = "<%" >> *space_p >>( code_sequence - "%>") >> "%>";

			/* Script Grammar */

			script_term  = ((print_p | punct_p | space_p)  - close);
			open		 = ch_p('<') >> *space_p >> str_p("script ") >> *space_p >> "runat" >> *space_p >> "=" >> 
						*space_p >>"\"template\"" >> *space_p >> ch_p('>');

			content		 = +(script_term);

			close        = ch_p('<') >>  "/script" >> ch_p('>');

			script       = open >> *(content - close)[push_back(var(self.tokens), construct_<Token>(ScriptBlock, construct_<std::string>(arg1, arg2)))]  >> close;
			//script_top   = script;
			//script_top   = script | (script || +space_p);

			/* Include Grammar */
			quoted_filename       =  (confix_p('\"', (+include_term)[push_back(var(self.tokens), construct_<Token>(IncludeFile, construct_<std::string>(arg1, arg2)))], '\"'));
			include_term		  = (print_p - (ch_p('\\') | '/' | '<' | '>' | '?' | '*'));
			includetag		      =   ("!--" >> *space_p >> "#" >> *space_p  >> "include " >> ("file" || *space_p) >> ("=" || *space_p) 
									>> quoted_filename >> *space_p >> ("--") );
			include_top			  = "<" >> (includetag - '>') >>  ">";

			/* Expression Grammar */
			expression		= ch_p('=') >> *space_p >> (+(print_p - "%>"))[push_back(var(self.tokens), construct_<Token>(Expression, construct_<std::string>(arg1, arg2)))] >> *space_p;
			expression_top  = "<%" >> ( expression - "%>") >> "%>";

			/* Comment Grammar */

			comment_term = (print_p | punct_p | space_p) - str_p("--");
			comment      = (+(comment_term - str_p("--")))[push_back(var(self.tokens), construct_<Token>(Comment, construct_<std::string>(arg1, arg2)))];
			commenttag   = ("--" >> *space_p >> comment >> *space_p >> ("--" ) ) >>  *space_p;
			comment_top	 = ("<%" >> (commenttag - "%>") >> "%>");

			/* Starting Rule */
			markup_top       = (script | directive_top | comment_top | include_top | expression_top | code_top);
				
			start_point = top;
		}

		typedef rule<ScannerT> rule_t;
		// Starting rules
		rule_t  start_point;
		rule_t top;
		rule_t markup_top;
		
		// Comments
		rule_t comment_top;
		rule_t commenttag;
		rule_t comment;
		rule_t comment_term;
		
		// Expressions
		rule_t expression_top;
		rule_t expression;

		// Include directives
		rule_t include_top;
		rule_t includetag;
		rule_t include_term;
		rule_t quoted_filename;

		// Script Subrules
		rule_t script;
		rule_t close;
		rule_t content;
		rule_t open;
		rule_t script_term;

		// Literal Code
		rule_t code_top;
		rule_t code_sequence;

		// Directives
		rule_t directive_top;
		rule_t attr_value;
		rule_t attr_name;
		rule_t attribute;
		rule_t directive_name;
		rule_t directive;
		rule_t directive_term;

		// Literal Text
		rule_t literal_sequence;
		rule_t literal_chars;
		rule_t literal_char;

        rule<ScannerT> const& start() const { return start_point; }
    };
    std::vector<Token>& tokens;
};